from tkinter import Image

import cv2
import numpy as np

from PIL import Image, ImageDraw, ImageFont

def_font = None


def cv2PutChineseText(img, text, position, font_size=20, color=(255, 255, 255)):
    """
    在OpenCV图像上绘制中文文字（解决乱码问题）

    参数:
        img: OpenCV图像（BGR格式）
        text: 要绘制的中文文字
        position: 文字左上角坐标 (x, y)
        font_size: 字体大小
        color: 文字颜色（BGR格式，默认白色）

    返回:
        绘制了文字的图像
    """
    # 转换为PIL图像（RGB格式）
    img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

    # 加载中文字体（需确保字体文件存在）
    # 这里使用系统默认中文字体，也可指定字体文件路径
    try:
        # 尝试加载系统中的黑体字体（Windows/Linux/macOS通用）
        # todo 黑体是有版权的,实际使用时请更换
        font = ImageFont.truetype("simhei.ttf", font_size) if def_font is None else def_font
    except IOError:
        try:
            # 备选字体（macOS）
            font = ImageFont.truetype("Heiti TC", font_size)
        except IOError:
            # 若找不到字体，使用默认字体（可能仍会乱码）
            font = ImageFont.load_default()
            print("警告：未找到中文字体，可能显示乱码")

    # 绘制文字
    draw = ImageDraw.Draw(img_pil)
    draw.text(position, text, font=font, fill=(color[2], color[1], color[0]))  # 转换为RGB格式

    # 转回OpenCV格式（BGR）
    return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)


# 是否可以选择区域
canSelectRegion = False
# 全局变量存储选择区域坐标
roiCoordinates = {'x1': 0, 'y1': 0, 'x2': 0, 'y2': 0}
drawing = False  # 标记是否正在绘制


def selectRegion(event, x, y, flags, param):
    if not canSelectRegion:
        return
    """ 鼠标回调函数，处理区域选择 """
    global roiCoordinates, drawing

    if event == cv2.EVENT_LBUTTONDOWN:  # 鼠标左键按下
        drawing = True
        roiCoordinates = {'x1': x, 'y1': y, 'x2': x, 'y2': y}

    elif event == cv2.EVENT_MOUSEMOVE:  # 鼠标移动
        if drawing:
            roiCoordinates['x2'] = x
            roiCoordinates['y2'] = y

    elif event == cv2.EVENT_LBUTTONUP:  # 鼠标左键释放
        drawing = False
        roiCoordinates['x2'] = x
        roiCoordinates['y2'] = y


def checkRegion(frame):
    global roiCoordinates, drawing, canSelectRegion  # 添加全局声明
    canSelectRegion = True
    cv2.destroyAllWindows()
    kFrame = cv2PutChineseText(frame, "q:退出截取;r:截取重置;空格:确认", (10, 10), 20)
    clone = kFrame.copy()
    # 创建窗口并绑定鼠标事件
    cv2.namedWindow('Select ROI')
    cv2.setMouseCallback('Select ROI', selectRegion)
    cv2.moveWindow('Select ROI', 100, 100)
    while True:
        current_frame = clone.copy()
        # 实时绘制选择框
        if roiCoordinates['x1'] != roiCoordinates['x2'] and roiCoordinates['y1'] != roiCoordinates['y2']:
            cv2.rectangle(current_frame,
                          (roiCoordinates['x1'], roiCoordinates['y1']),
                          (roiCoordinates['x2'], roiCoordinates['y2']),
                          (0, 255, 0), 2)

            # 显示坐标信息
            info = f"X: {min(roiCoordinates['x1'], roiCoordinates['x2'])}-{max(roiCoordinates['x1'], roiCoordinates['x2'])}"
            info2 = f"Y: {min(roiCoordinates['y1'], roiCoordinates['y2'])}-{max(roiCoordinates['y1'], roiCoordinates['y2'])}"
            cv2.putText(current_frame, info, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
            cv2.putText(current_frame, info2, (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
        cv2.imshow('Select ROI', current_frame)

        # 键盘控制
        kkey = cv2.waitKey(1) & 0xFF

        # 按R重置选择
        if kkey == ord('r'):
            clone = kFrame.copy()
            initRoiCoordinates()
        # 按Q确认并退出
        elif kkey == ord('q'):
            canSelectRegion = False
            cv2.destroyAllWindows()
            return False
        elif kkey == ord(' '):
            canSelectRegion = False
            cv2.destroyAllWindows()
            return True


# 初始化选择框
def initRoiCoordinates():
    global roiCoordinates
    roiCoordinates = {'x1': 0, 'y1': 0, 'x2': 0, 'y2': 0}


def roiCoordinatesToRect():
    global roiCoordinates
    return roiCoordinates['x1'], roiCoordinates['y1'], roiCoordinates['x2'] - roiCoordinates['x1'], roiCoordinates[
        'y2'] - roiCoordinates['y1']


# 目标追踪窗口位置
track_window = None
roi_hist = None


def handlerTrackWindow(frame):
    global track_window, roi_hist
    # 设置初始窗口 (x, y, width, height)
    x, y, w, h = roiCoordinatesToRect()
    track_window = (x, y, w, h)

    # 设置 ROI (Region of Interest)
    roi = frame[y:y + h, x:x + w]

    # 转换为 HSV 颜色空间
    hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)

    # 创建掩膜并计算直方图
    mask = cv2.inRange(hsv_roi, np.array((0., 60., 32.)), np.array((180., 255., 255.)))
    roi_hist = cv2.calcHist([hsv_roi], [0], mask, [180], [0, 180])
    cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)


def start():
    global track_window, roi_hist, roiCoordinates
    cv2.namedWindow('Video')
    cv2.moveWindow('Video', 100, 100)
    # 是否重新加载跟踪
    canReloadCamShift = False
    canUseRoiCoordinates = False
    # 设置跟踪终止条件
    # type: 典型经验值(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT)，何时调大(永远这么写即可)，备注(两个条件任一满足即停止)
    # maxCount: 典型经验值(10~20)，何时调大(目标运动快、帧率低)，何时调小(实时性要求高、目标运动慢)，备注(>30 通常无意义)
    # epsilon: 典型经验值(1~2 像素)，何时调大(目标很大或实时优先)，何时调小(目标很小或需高精度)，备注(设为 0 等价于仅用 maxCount 控制)
    term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
    cap = cv2.VideoCapture(0)
    while True:
        ret, frame = cap.read()
        rootFrame = frame.copy()
        if not ret:
            break
        if canReloadCamShift:
            handlerTrackWindow(frame)
        if canUseRoiCoordinates:
            # 转换为 HSV 颜色空间
            hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

            # 计算反向投影
            dst = cv2.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)

            # 应用 CamShift 算法
            ret, track_window = cv2.CamShift(dst, track_window, term_crit)

            # 绘制跟踪结果
            pts = cv2.boxPoints(ret)
            pts = np.int_(pts)
            img2 = cv2.polylines(frame, [pts], True, 255, 2)
            baseFrame = cv2PutChineseText(img2, "q:退出;空格:截取;r:重置", (10, 10), 20)
        else:
            baseFrame = cv2PutChineseText(frame, "q:退出;空格:截取;r:重置", (10, 10), 20)

        cv2.imshow('Video', baseFrame)
        rootKey = cv2.waitKey(1) & 0xFF
        if rootKey == ord(' ') and not canSelectRegion:
            initRoiCoordinates()
            if checkRegion(rootFrame):
                canReloadCamShift = True
                canUseRoiCoordinates = True
        elif rootKey == ord('q'):
            break
        elif rootKey == ord('r'):
            initRoiCoordinates()
            canReloadCamShift=False
            canUseRoiCoordinates = False
    cv2.destroyAllWindows()
    cap.release()


if __name__ == '__main__':
    start()
